{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# Walking Kodee\n",
    "\n",
    "This notebook shows an animated Kodee sprite walking on the bottom of the JetBrains IDE window.\n",
    "\n",
    "It uses a custom Swing component that loads an animated GIF resource and renders it as an animation.\n",
    "\n",
    "Additionally, it demonstrates how to remove the animation by properly disposing of resources when the Kotlin Notebook kernel is restarted."
   ]
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "%use intellij-platform",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "import com.intellij.openapi.application.runInEdt\n",
    "import com.intellij.openapi.util.Disposer\n",
    "import com.intellij.openapi.wm.WindowManager\n",
    "import java.awt.Dimension\n",
    "import java.awt.Graphics\n",
    "import java.net.URL\n",
    "import javax.imageio.ImageIO\n",
    "import javax.imageio.ImageReader\n",
    "import javax.swing.JLabel\n",
    "import kotlin.concurrent.thread\n",
    "\n",
    "private inline fun <T> ImageReader.use(block: (ImageReader) -> T): T = try {\n",
    "    block(this)\n",
    "} finally {\n",
    "    dispose()\n",
    "}\n",
    "\n",
    "class AnimatedGifLabel(private val gifUrl: URL, private val parentWidth: Int) : JLabel() {\n",
    "    private var currentFrame = 0\n",
    "    private var xPosition = 0\n",
    "    private val frames by lazy {\n",
    "        gifUrl.openStream().use { stream ->\n",
    "            ImageIO.getImageReadersByFormatName(\"gif\").next().use { reader ->\n",
    "                reader.input = ImageIO.createImageInputStream(stream)\n",
    "                (0 until reader.getNumImages(true)).map { reader.read(it) }\n",
    "            }\n",
    "        }\n",
    "    }\n",
    "\n",
    "    init {\n",
    "        frames.firstOrNull()?.apply {\n",
    "            preferredSize = Dimension(width / 2, height / 2)\n",
    "        }\n",
    "    }\n",
    "\n",
    "    override fun paintComponent(g: Graphics) {\n",
    "        g.drawImage(frames[currentFrame], 0, 0, width, height, null)\n",
    "    }\n",
    "\n",
    "    fun startAnimation() = thread {\n",
    "        generateSequence(0) { (it + 1) % frames.size }.forEach { frameIndex ->\n",
    "            currentFrame = frameIndex\n",
    "            repaint()\n",
    "            Thread.sleep(40)\n",
    "            xPosition += 3\n",
    "\n",
    "            if (xPosition > parentWidth) {\n",
    "                xPosition = -width  // Start from left edge again\n",
    "            }\n",
    "\n",
    "            setLocation(xPosition, y)\n",
    "        }\n",
    "    }\n",
    "}\n",
    "\n",
    "runInEdt {\n",
    "    val project = currentProject()\n",
    "    val frame = WindowManager.getInstance().getFrame(project) ?: error(\"IDE frame not found\")\n",
    "    val kodee = javaClass.getResource(\"walking_kodee/kodee.gif\") ?: error(\"kodee.gif not found\")\n",
    "\n",
    "    val label = AnimatedGifLabel(kodee, frame.width).apply {\n",
    "        setBounds(\n",
    "            0,\n",
    "            frame.height - preferredSize.height - 32,\n",
    "            preferredSize.width,\n",
    "            preferredSize.height,\n",
    "        )\n",
    "        startAnimation()\n",
    "    }\n",
    "\n",
    "    val layeredPane = frame.layeredPane.apply { add(label) }\n",
    "\n",
    "    Disposer.register(notebookDisposable) {\n",
    "        layeredPane.apply {\n",
    "            remove(label)\n",
    "            revalidate()\n",
    "            repaint()\n",
    "        }\n",
    "    }\n",
    "}"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "Loading `Kodee.gif` from resources is available only in IntelliJ IDEA 2025.2+.\n",
    "\n",
    "For a backward compatibility, you may use:\n",
    "```kotlin\n",
    "val kodee = notebook.workingDir.resolve(\"Kodee.gif\").toUri().toURL()\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Kotlin",
   "language": "kotlin",
   "name": "kotlin"
  },
  "language_info": {
   "name": "kotlin",
   "version": "2.2.20-dev-7701",
   "mimetype": "text/x-kotlin",
   "file_extension": ".kt",
   "pygments_lexer": "kotlin",
   "codemirror_mode": "text/x-kotlin",
   "nbconvert_exporter": ""
  },
  "ktnbPluginMetadata": {
   "sessionRunMode": "IDE_PROCESS"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
